﻿using System;
using System.Collections.Generic;
using System.Linq;
using AcademicPlanner.Data;

namespace AcademicPlanner.Domain
{
	/// <summary>
	/// Model is the base class for all models in the domain layer of
	/// the AcademicPlaner application. Models are business logic entities
	/// with business logic operations. They are wrapped around <c>Entity</c>s
	/// who represent their data aspect.
	/// </summary>
	/// <version>1.0</version>
	/// <since>1.0</since>
    public class Model
    {
		/// <summary>
		/// The <c>Entity</c> of this Model representing its data.
		/// </summary>
		/// <version>1.0</version>
		/// <since>1.0</since>
		protected Entity entity;


		/// <summary>
		/// Gets the <c>DomainMaster</c> of the AcademicPlanner application.
		/// </summary>
		/// <version>1.0</version>
		/// <since>1.0</since>
		protected DomainMaster DomainMaster { get { return DomainMaster.GetInstance(); } }

		/// <summary>
		/// Gets this Model's <c>Entity</c>'s <c>EntityRepository</c>.
		/// </summary>
		/// <version>1.0</version>
		/// <since>1.0</since>
		protected virtual EntityRepository EntityRepo { get; private set; }

		/// <summary>
		/// Gets this Model's identifier.
		/// </summary>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public int EntityId { get { return entity.Id; } }


		/// <summary>
		/// No argument constructor; does nothing.
		/// </summary>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public Model()
		{
			
		}

		/// <summary>
		/// Constructs a Model based on an <c>Entity</c>. This constructor
		/// simply initializes this Model's <c>entity</c> field.
		/// </summary>
		/// <param name="pEntity">The <c>Entity</c> representing the Model's data.</param>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public Model(Entity pEntity)
		{
			Init(pEntity);
		}


		/// <summary>
		/// Initializes this Model's <c>entity</c> field. It does so only
		/// if the <c>entity</c> field is currently <c>null</c>. The initialization
		/// can be customized by descendants by overriding <see cref="M:PostInit"/>.
		/// </summary>
		/// <param name="pEntity">The <c>Entity</c> representing the Model's data.</param>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public void Init(Entity pEntity)
		{
			if (entity == null)
			{
				entity = pEntity;
				PostInit();
			}
		}

		/// <summary>
		/// Called by <see cref="M:Init"/> to allow descendants to customize the
		/// initialization.
		/// </summary>
		/// <version>1.0</version>
		/// <since>1.0</since>
		protected virtual void PostInit() { }

		/// <summary>
		/// Persists this Model's data to the data storage. This method relies
		/// on this Model's <c>EntityRepo</c> to do the job.
		/// </summary>
		/// <exception cref="T:CriticalDomainException">
		/// Thrown with an error code of <c>DomainErrorCode.G_SAVE_ENTITY_FAILURE</c>
		/// when a <see cref="T:AcademicPlanner.Data.DataException"/> occurs;
		/// the <see cref="T:AcademicPlanner.Data.DataException"/> is accessible through
		/// <c>InnerException</c> property.
		/// </exception>
		/// <seealso cref="M:AcademicPlanner.Data.EntityRepository.Save"/>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public virtual void Persit()
		{
			try
			{
				EntityRepo.Save(entity);
			}
			catch (DataException vDx)
			{
				throw new CriticalDomainException(DomainErrorCode.G_SAVE_ENTITY_FAILURE, vDx);
			}
		}

		/// <summary>
		/// Gets the <c>entity</c> of another <c>Model</c>. This method is a hack to
		/// allow a Model (and only a Model) to access another peer Model's <c>entity</c>; no built in
		/// access modifier solved the problem.
		/// </summary>
		/// <param name="pModel">The Model whose <c>entity</c> to get.</param>
		/// <returns>The Model's <c>entity</c>.</returns>
		/// <version>1.0</version>
		/// <since>1.0</since>
		protected static Entity GetOtherModelEntity(Model pModel)
		{
			return pModel.entity;
		}
    }
}
